home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / proc / procTimer.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  15KB  |  524 lines

  1. /* 
  2.  * procTimer.c --
  3.  *
  4.  *    Routines to manipulate the interval timers of a process.
  5.  *
  6.  * Copyright 1987, 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procTimer.c,v 9.4 92/04/10 16:40:53 kupfer Exp $ SPRITE (Berkeley)";
  18. #endif /* not lint */
  19.  
  20.  
  21. #include <sprite.h>
  22. #include <proc.h>
  23. #include <procInt.h>
  24. #include <timer.h>
  25. #include <sys.h>
  26. #include <sig.h>
  27. #include <stdlib.h>
  28. #include <sync.h>
  29. #include <bstring.h>
  30.  
  31. /*
  32.  * Information about the state of an interval timer for a process.
  33.  * There are several types of interval timers and the info for all of
  34.  * them are kept in an array pointed to by the PCB entry.
  35.  */
  36. typedef struct ProcIntTimerInfo {
  37.     Timer_Ticks    expire;        /* When the timer is to expire. */
  38.     Timer_Ticks    interval;    /* Amount of time between expirations.
  39.                  * If zero, then the timer is not restarted 
  40.                  * after the first expiration. */
  41.     ClientData    token;        /* Token returned by Proc_CallFuncAbsTime. 
  42.                  * Used by the signal-sending func to check if
  43.                  * a timer has been cancelled. */
  44. } ProcIntTimerInfo;
  45.  
  46.  
  47. /*
  48.  * Monitor lock to serialize access to timer callback queue elements.
  49.  * This could be changed to a per-process monitor lock if contention is a
  50.  * problem.  To avoid deadlock, routines that lock a PCB should lock the
  51.  * PCB before acquiring the monitor lock.
  52.  */
  53. static Sync_Lock    procTimerLock = Sync_LockInitStatic("procTimerLock");
  54. #define    LOCKPTR &procTimerLock
  55.  
  56. static ReturnStatus    GetCurrentTimer _ARGS_((Proc_ControlBlock *procPtr,
  57.                 int timerType, Proc_TimerInterval *timerBufPtr,
  58.                 Boolean userMode));
  59. static void        SendTimerSigFunc _ARGS_((ClientData data,
  60.                 Proc_CallInfo *infoPtr));
  61.  
  62.  
  63.  
  64. /*
  65.  *----------------------------------------------------------------------
  66.  *
  67.  * Proc_GetIntervalTimer --
  68.  *
  69.  *    Retrieves the current value of the interval timer. If the timer
  70.  *    is not set, zero time values are returned.
  71.  *    
  72.  *
  73.  * Results:
  74.  *    SUCCESS            - the timer value was returned.
  75.  *    GEN_INVALID_ARG        - unknown timer type.
  76.  *    SYS_ARG_NOACCESS    - the timer value could not be accessed.
  77.  *
  78.  * Side effects:
  79.  *    None.
  80.  *
  81.  *----------------------------------------------------------------------
  82.  */
  83.  
  84. ENTRY ReturnStatus
  85. Proc_GetIntervalTimer(timerType, userTimerPtr)
  86.     int            timerType;    /* What type of timer: one of 
  87.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  88.                      * PROC_TIMER_PROFILE. */
  89.     Proc_TimerInterval    *userTimerPtr;    /* Buffer to store the current value 
  90.                      * of the interval timer. */
  91. {
  92.     ReturnStatus status;
  93.     Proc_ControlBlock    *procPtr;
  94.  
  95.     if (timerType < 0 || timerType > PROC_MAX_TIMER) {
  96.     return(GEN_INVALID_ARG);
  97.     }
  98.  
  99.     if (userTimerPtr == USER_NIL) {
  100.     return(SYS_ARG_NOACCESS);
  101.     }
  102.  
  103.     procPtr = Proc_GetEffectiveProc();
  104.     if (procPtr == (Proc_ControlBlock *) NIL) {
  105.     panic("Proc_GetIntervalTime: current procPtr == NIL\n");
  106.     /*
  107.      * Just in case someone tries to continue.
  108.      */
  109.     return(FAILURE);
  110.     }
  111.  
  112.     Proc_Lock(procPtr);
  113.     LOCK_MONITOR;
  114.     status = GetCurrentTimer(procPtr, timerType, userTimerPtr, TRUE);
  115.     UNLOCK_MONITOR;
  116.     Proc_Unlock(procPtr);
  117.  
  118.     return(status);
  119. }
  120.  
  121. /*
  122.  *----------------------------------------------------------------------
  123.  *
  124.  * GetCurrentTimer --
  125.  *
  126.  *    An internal routine to get the current value of an interval timer
  127.  *    and copy it to the user's address space.
  128.  *
  129.  *    Note: this routine assumes the process's proc table entry is locked.
  130.  *
  131.  * Results:
  132.  *    SUCCESS            - the timer value was returned.
  133.  *    SYS_ARG_NOACCESS    - the timer value could not be accessed.
  134.  *
  135.  * Side effects:
  136.  *    None.
  137.  *
  138.  *----------------------------------------------------------------------
  139.  */
  140.  
  141. static INTERNAL ReturnStatus
  142. GetCurrentTimer(procPtr, timerType, timerBufPtr, userMode)
  143.     Proc_ControlBlock    *procPtr;    /* Process to get the timer value 
  144.                      * from. */
  145.     int            timerType;    /* What type of timer: one of 
  146.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  147.                      * PROC_TIMER_PROFILE. */
  148.     Proc_TimerInterval    *timerBufPtr;    /* Buffer to store the current value 
  149.                      * of the interval timer. */
  150.     Boolean        userMode;    /* TRUE if timerBufPtr is in user
  151.                      * space (normal case).  FALSE
  152.                      * for encapsulation.
  153.                      */
  154. {
  155.     register ProcIntTimerInfo    *timerPtr = (ProcIntTimerInfo *) NIL;
  156.     Proc_TimerInterval    timer;
  157.     Boolean exists = FALSE;
  158.  
  159.     if (procPtr->timerArray != (ProcIntTimerInfo  *) NIL) {
  160.     timerPtr = &procPtr->timerArray[timerType];
  161.     if (timerPtr->token != (ClientData) NIL) {
  162.         exists = TRUE;
  163.     }
  164.     }
  165.     if (!exists) {
  166.  
  167.     /*
  168.      * No timer is scheduled. Just return zero values.
  169.      */
  170.     timer.interval.seconds = 0;
  171.     timer.interval.microseconds = 0;
  172.     timer.curValue.seconds = 0;
  173.     timer.curValue.microseconds = 0;
  174.     } else {
  175.     Timer_Ticks temp;
  176.  
  177.     Timer_TicksToTime(timerPtr->interval, &timer.interval);
  178.  
  179.     /*
  180.      * Get the amount of time remaining before the timer's expiration.
  181.      */
  182.     Timer_GetCurrentTicks(&temp);
  183.     Timer_SubtractTicks(timerPtr->expire, temp, &temp);
  184.     Timer_TicksToTime(temp, &timer.curValue);
  185.     }
  186.  
  187.     if (userMode) {
  188.     if (Proc_ByteCopy(FALSE, sizeof(timer), 
  189.               (Address) &timer, (Address) timerBufPtr) != SUCCESS) {
  190.         return(SYS_ARG_NOACCESS);
  191.     }
  192.     } else {
  193.     bcopy((Address) &timer, (Address) timerBufPtr, sizeof(timer));
  194.     }
  195.  
  196.     return(SUCCESS);
  197. }
  198.  
  199. /*
  200.  *----------------------------------------------------------------------
  201.  *
  202.  * Proc_SetIntervalTimer --
  203.  *
  204.  *    Start or cancel an interval timer for a process.  This is the
  205.  *    system call version, which calls a more general routine.
  206.  *
  207.  * Results:
  208.  *    SUCCESS            - the timer was started or stopped.
  209.  *    GEN_INVALID_ARG        - unknown timer type or invalid time value.
  210.  *    SYS_ARG_NOACCESS    - a timer value could not be accessed.
  211.  *
  212.  * Side effects:
  213.  *    A CallFunc process might be scheduled. The process's PCB entry
  214.  *    is updated.
  215.  *
  216.  *----------------------------------------------------------------------
  217.  */
  218.  
  219. ENTRY ReturnStatus
  220. Proc_SetIntervalTimer(timerType, newTimerPtr, oldTimerPtr)
  221.     int            timerType;    /* What type of timer: one of 
  222.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  223.                      * PROC_TIMER_PROFILE. */
  224.     Proc_TimerInterval    *newTimerPtr;    /* Buffer that holds a new value for
  225.                      * the interval timer. */
  226.     Proc_TimerInterval    *oldTimerPtr;    /* Buffer to hold the former value of
  227.                      * the timer. */
  228. {
  229.     return(ProcChangeTimer(timerType, newTimerPtr, oldTimerPtr, TRUE));
  230. }
  231.  
  232.  
  233. /*
  234.  *----------------------------------------------------------------------
  235.  *
  236.  * ProcChangeTimer --
  237.  *
  238.  *    Start or cancel an interval timer for a process.  This can
  239.  *    be called from kernel mode as well as user mode.
  240.  *
  241.  * Results:
  242.  *    SUCCESS            - the timer was started or stopped.
  243.  *    GEN_INVALID_ARG        - unknown timer type or invalid time value.
  244.  *    SYS_ARG_NOACCESS    - a timer value could not be accessed.
  245.  *
  246.  * Side effects:
  247.  *    A CallFunc process might be scheduled. The process's PCB entry
  248.  *    is updated.
  249.  *
  250.  *----------------------------------------------------------------------
  251.  */
  252.  
  253. ENTRY ReturnStatus
  254. ProcChangeTimer(timerType, newTimerPtr, oldTimerPtr, userMode)
  255.     int            timerType;    /* What type of timer: one of 
  256.                      * PROC_TIMER_REAL, PROC_TIMER_VIRTUAL,
  257.                      * PROC_TIMER_PROFILE. */
  258.     Proc_TimerInterval    *newTimerPtr;    /* Buffer that holds a new value for
  259.                      * the interval timer. */
  260.     Proc_TimerInterval    *oldTimerPtr;    /* Buffer to hold the former value of
  261.                      * the timer. */
  262.     Boolean        userMode;    /* TRUE if intervals are in user
  263.                      * space (normal case).  FALSE
  264.                      * for encapsulation.
  265.                      */
  266. {
  267.     register ProcIntTimerInfo    *timerPtr;
  268.     register Proc_ControlBlock    *procPtr;
  269.     Proc_TimerInterval    newTimer;
  270.  
  271.     if (timerType < 0 || timerType > PROC_MAX_TIMER) {
  272.     return(GEN_INVALID_ARG);
  273.     }
  274.  
  275.     procPtr = Proc_GetEffectiveProc();
  276.     if (procPtr == (Proc_ControlBlock *) NIL) {
  277.     panic("Proc_SetIntervalTime: procPtr == NIL\n");
  278.     return(FAILURE);
  279.     }
  280.  
  281.     Proc_Lock(procPtr);
  282.     LOCK_MONITOR;
  283.  
  284.     if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  285.     int j;
  286.  
  287.     /*
  288.      * The table hasn't been initialized yet. Allocate enough entries
  289.      * for all the timers. The memory won't be deallocated when the
  290.      * process dies so it can be reused by the next process using the
  291.      * PCB entry.
  292.      */
  293.     procPtr->timerArray = (ProcIntTimerInfo  *)
  294.         malloc(sizeof(ProcIntTimerInfo) * (PROC_MAX_TIMER +1));
  295.  
  296.     for (j = 0; j <= PROC_MAX_TIMER; j++) {
  297.         procPtr->timerArray[j].token = (ClientData) NIL;
  298.     }
  299.     }
  300.     timerPtr = &procPtr->timerArray[timerType];
  301.  
  302.     /*
  303.      * Return the current value if the user wants it.
  304.      */
  305.     if (oldTimerPtr != USER_NIL) {
  306.     if (GetCurrentTimer(procPtr, timerType, oldTimerPtr,
  307.                 userMode) != SUCCESS) {
  308.         Proc_Unlock(procPtr);
  309.         UNLOCK_MONITOR;
  310.         return(SYS_ARG_NOACCESS);
  311.     }
  312.     }
  313.  
  314.     /*
  315.      * Copy the new timer value from user space or a kernel buffer.
  316.      */
  317.     if (userMode) {
  318.     if (Proc_ByteCopy(TRUE, sizeof(newTimer), 
  319.         (Address) newTimerPtr, (Address) &newTimer) != SUCCESS) {
  320.         Proc_Unlock(procPtr);
  321.         UNLOCK_MONITOR;
  322.         return(SYS_ARG_NOACCESS);
  323.     }
  324.     } else {
  325.     bcopy((Address) newTimerPtr, (Address) &newTimer, sizeof(newTimer));
  326.     }
  327.  
  328.     if ((newTimer.curValue.seconds == 0) && 
  329.     (newTimer.curValue.microseconds == 0)) {
  330.  
  331.     /*
  332.      * The user wants to cancel the timer.  Invalidate the token for
  333.      * the existing expiration routine, and cancel the timer.
  334.      */
  335.     if (timerPtr->token != (ClientData) NIL) {
  336.         Proc_CancelCallFunc(timerPtr->token);
  337.         timerPtr->token = (ClientData) NIL;
  338.     }
  339.     } else {
  340.     Timer_Ticks curTime;
  341.  
  342.     /*
  343.      * Make sure the times are valid and within the clock's resolution.
  344.      */
  345.     if ((newTimer.curValue.seconds < 0) || 
  346.         (newTimer.curValue.microseconds < 0) ||
  347.         (newTimer.curValue.microseconds > ONE_SECOND) ||
  348.         (newTimer.interval.seconds < 0) || 
  349.         (newTimer.interval.microseconds < 0) ||
  350.         (newTimer.interval.microseconds > ONE_SECOND)) {
  351.  
  352.         Proc_Unlock(procPtr);
  353.         UNLOCK_MONITOR;
  354.         return(GEN_INVALID_ARG);
  355.     }
  356.     if ((newTimer.curValue.seconds == 0) && 
  357.         (newTimer.curValue.microseconds < TIMER_CALLBACK_INTERVAL_APPROX)) {
  358.         newTimer.curValue.microseconds = TIMER_CALLBACK_INTERVAL_APPROX;
  359.     }
  360.     if ((newTimer.interval.seconds == 0) && 
  361.         (newTimer.interval.microseconds > 0) &&
  362.         (newTimer.interval.microseconds < TIMER_CALLBACK_INTERVAL_APPROX)) {
  363.         newTimer.interval.microseconds = TIMER_CALLBACK_INTERVAL_APPROX;
  364.     }
  365.  
  366.     Timer_TimeToTicks(newTimer.interval, &timerPtr->interval);
  367.     Timer_TimeToTicks(newTimer.curValue, &timerPtr->expire);
  368.  
  369.     Timer_GetCurrentTicks(&curTime);
  370.     Timer_AddTicks(curTime, timerPtr->expire, &timerPtr->expire);
  371.  
  372.     /*
  373.      * Setting the token implicitly cancels a previous expiration
  374.      * routine's callback, but let's clear the old one to avoid
  375.      * putting cruft in the timer queue.
  376.      */
  377.     if (timerPtr->token != (ClientData) NIL) {
  378.         Proc_CancelCallFunc(timerPtr->token);
  379.     }
  380.     timerPtr->token = Proc_CallFuncAbsTime(SendTimerSigFunc, 
  381.             (ClientData) procPtr->processID, timerPtr->expire);
  382.     }
  383.  
  384.     Proc_Unlock(procPtr);
  385.     UNLOCK_MONITOR;
  386.     return(SUCCESS);
  387. }
  388.  
  389. /*
  390.  *----------------------------------------------------------------------
  391.  *
  392.  * SendTimerSigFunc --
  393.  *
  394.  *    Called when one of a process's interval timers has expired. This
  395.  *    routine sends a SIG_TIMER signal to process (the signal subcode is
  396.  *    the timer type, as defined in user/proc.h).
  397.  *
  398.  *
  399.  * Results:
  400.  *    None.
  401.  *
  402.  * Side effects:
  403.  *    A signal is sent to the process. A call-func process may be 
  404.  *    scheduled to send a signal in the future.
  405.  *
  406.  *----------------------------------------------------------------------
  407.  */
  408.  
  409. static ENTRY void
  410. SendTimerSigFunc(data, infoPtr)
  411.     ClientData        data;        /* Really the ID of the process that
  412.                      * should get the signal. */
  413.     Proc_CallInfo    *infoPtr;    /* Used to compare the token in the
  414.                      * PCB entry to make sure the signal
  415.                      * is wanted. */
  416. {
  417.     Proc_ControlBlock    *procPtr;
  418.     register ProcIntTimerInfo    *timerPtr;
  419.     int i;
  420.  
  421.     /*
  422.      * If the process has died, the procPtr will be NIL.
  423.      */
  424.     procPtr = Proc_LockPID((Proc_PID) data);
  425.     if (procPtr == (Proc_ControlBlock *) NIL) {
  426.     return;
  427.     }
  428.  
  429.     LOCK_MONITOR;
  430.  
  431.     /*
  432.      * Scan all the timer state info to see which timer expired.
  433.      * If our token matches the one in the timer table entry, then
  434.      * send the signal (the subcode is the timer type). If there's no 
  435.      * match after scanning all the timers, then the user cancelled the
  436.      * a timer so there's nothing to do.
  437.      */
  438.     for (i = 0; i <= PROC_MAX_TIMER; i++) {
  439.     
  440.     if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  441.         /*
  442.          * This should not happen: why did we get scheduled if
  443.          * there aren't any timers?
  444.          */
  445.         panic("SendTimerSigFunc: null timer table!\n");
  446.         break;
  447.     }
  448.     
  449.     timerPtr = &procPtr->timerArray[i];
  450.     if (timerPtr->token == infoPtr->token) {
  451.         (void) Sig_SendProc(procPtr, SIG_TIMER, i, (Address)0);
  452.         
  453.         /*
  454.          * See if the signal is supposed to be repeated in the future.
  455.          */
  456.         if (Timer_TickEQ(timerPtr->interval, timer_TicksZeroSeconds)){
  457.         /*
  458.          * Nope -- all done.
  459.          */
  460.         timerPtr->token = (ClientData) NIL;
  461.         } else {
  462.         /*
  463.          * A signal is wanted in "interval" seconds from now.
  464.          * Add the interval to the expiration time instead of
  465.          * the current time to prevent drift.
  466.          */
  467.         
  468.         Timer_AddTicks(timerPtr->interval, timerPtr->expire, 
  469.                    &timerPtr->expire);
  470.         timerPtr->token = Proc_CallFuncAbsTime(SendTimerSigFunc,
  471.                                data, timerPtr->expire);
  472.         }
  473.         break;
  474.     }
  475.     }
  476.  
  477.     UNLOCK_MONITOR;
  478.     Proc_Unlock(procPtr);
  479. }
  480.  
  481.  
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * ProcDeleteTimers --
  486.  *
  487.  *    Cancel all interval timers for a process.  Performed on exit.
  488.  *    ProcPtr is assumed to be locked on entry.
  489.  *
  490.  * Results:
  491.  *    None.
  492.  *
  493.  * Side effects:
  494.  *    A CallFunc process might be descheduled. 
  495.  *
  496.  *----------------------------------------------------------------------
  497.  */
  498.  
  499. ENTRY void
  500. ProcDeleteTimers(procPtr)
  501.     register Proc_ControlBlock    *procPtr;
  502. {
  503.     register ProcIntTimerInfo    *timerPtr;
  504.     int j;
  505.  
  506.     LOCK_MONITOR;
  507.  
  508.     if (procPtr->timerArray == (ProcIntTimerInfo  *) NIL) {
  509.     goto done;
  510.     }
  511.  
  512.     for (j = 0; j <= PROC_MAX_TIMER; j++) {
  513.     timerPtr = &procPtr->timerArray[j];
  514.     if (timerPtr->token != (ClientData) NIL) {
  515.         Proc_CancelCallFunc(timerPtr->token);
  516.         timerPtr->token = (ClientData) NIL;
  517.     }
  518.     }
  519.  
  520.  done:
  521.     UNLOCK_MONITOR;
  522. }
  523.  
  524.